/**
  ******************************************************************************
  * @file           
  * @author         东方电科
  * @brief          iec104 基础应用，此处链路上作 tcp client 端，作 104 从站
  ******************************************************************************
  *
  * COPYRIGHT(c) 2019 GoodMorning
  *
  ******************************************************************************
  */

/* Includes -----------------------------------------------------------------*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>

#include <unistd.h>
#include <signal.h>
#include <sys/time.h>   //select time


#include <netinet/in.h>    // for sockaddr_in
#include <sys/types.h>    // for socket
#include <sys/socket.h>    // for socket
#include <arpa/inet.h>    //for inet_ntoa 

#include "iec10x.h"
#include "SIQ.h"

/* Private types ------------------------------------------------------------*/

/* Private macro ------------------------------------------------------------*/

#define SERVER_PORT            2404            //  define the defualt connect port id 
#define SERVER_IP              "192.168.0.212"

/* Private variables --------------------------------------------------------*/

static void iec104_puts(uint8_t * info , int len);

/* socket */
static int sockfd = 0;

/* 示例总线 */
struct iec10x demobus = {0};

/* 单点信息地址 */
static uint32_t SIQaddr[100] = { 0 };

/* 总线参数 */
static const struct iec104cfg ieccfg = {
	.haltx       = iec104_puts , ///< 设定总线的硬件输出
	.print       = printf      ,
	.dbglevel    = IEC_DBG_MIDDLE ,
	.cmnaddr     = 0x01        , ///< 设定总线的公共地址为 1
	.cmnaddrlen  = 2           , ///< 公共地址长度为 2
	.causelen    = 2           , ///< 传输原因长度为 2
	.infoaddrlen = 3           , ///< 信息体地址长度为 3
	.mode        = 0           , ///< 从站模式
	.period      = 60*1000     , ///< 主动上送周期为 60 x 1000 ms
};


/* Global  variables --------------------------------------------------------*/

/* Private function prototypes ----------------------------------------------*/

/* Gorgeous Split-line ------------------------------------------------------*/


/**
  * @brief    获取本地 SIQ 状态值，即地址列表和元素个数。
  * @note     一般作为从机时，被主机召唤后由协议栈调用
  * @author   古么宁
  * @param    devaddr  : 设备地址，对哪个设备进行操作
  * @param    addrlist : 数据结构体地址列表，必须为 uint16_t 数组
  * @param    info     : 数据结构体 info 的个数
  * @return   don't care
*/
int32_t SIQstate(void * bus,uint32_t ** addrlist,int * num)
{
	*addrlist = SIQaddr;
	*num = sizeof(SIQaddr)/sizeof(SIQaddr[0]);
	return 0;
}



int SIQ0T_get(void * bus, uint32_t  infoaddr , void * value )
{
	SIQ0T_t * val = value;
	
	val->SIQ.val = 0;
	return 0;
}

int SIQsctrl(void * bus,int COT,int nums ,uint32_t * addrlist , void * data)
{
	uint8_t ctrlcmd  = *(uint8_t *)data ;
	if (ctrlcmd & 0x80) {//遥控选择
		printf("teleconctrl select [0x%x] as %d.\r\n",addrlist[0],ctrlcmd);
	}
	else {
		printf("teleconctrl set [0x%x] as %d.\r\n",addrlist[0],ctrlcmd);
	}
	return 0;
}


void device_init(struct iec10x * bus)
{
	// 初始化不带时标的 SIQ 单点信息设备接口
	iec10x_devif_register(bus,M_SP_NA_1,SIQstate,SIQ0T_get,NULL);

	iec10x_devif_register(bus,C_SC_NA_1 ,NULL , NULL , SIQsctrl ); // 单点遥控命令设备接口，
}






static void iec104_puts(uint8_t * info , int len)
{
	if (sockfd) {
		write(sockfd , info , len);
	}
}



void * iec104_poll(void *arg)
{
	size_t time = 0;
	struct timeval timer50ms ;
	while( 1 ) {
		timer50ms.tv_sec = 0;
		timer50ms.tv_usec = 50000 ;
		select(0, NULL, NULL, NULL, &timer50ms);
		time += 50 ;
		// 处理协议栈 50ms 超时/定时任务，此处不需要太精确
		iec10x_periodic_handle(time); 
	}
}


void * socket_init(void * arg)
{
	unsigned char databuf[1024];
	int    datalen , clientsock;
	size_t len = sizeof(struct sockaddr_in);

	struct sockaddr_in server_addr = {0};

	server_addr.sin_family      = AF_INET;
	server_addr.sin_port        = htons(SERVER_PORT);
	server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
	
	while(1){
		if (!sockfd) {
			clientsock = socket(AF_INET,SOCK_STREAM,0);
			if( clientsock < 0)  {
				printf("Create Socket Failed!\n");
				clientsock = 0 ;
				sleep(5);
				continue;
			}

			if(connect(clientsock, (struct sockaddr *)&server_addr,len) < 0) {
				printf("Connect Failed!\n");
				close(clientsock);
				sleep(1);
				continue;
			}
			else {
				printf("Connect Success!\n");
				sockfd = clientsock ;
			}
		}

		datalen = read(sockfd,databuf,sizeof(databuf));
		if (datalen > 0) {
			iec10x_recv(&demobus,databuf,datalen);
		}
		else {
			close(sockfd);
			sockfd = 0;
			printf("remote disconnect.\r\n");
		}
	}
}




int main(int argc, char *argv[])
{
	pthread_t newthread;

	/* 初始化 iec 协议栈 */
	iec10x_protocol_init();
	
	/* 初始化总线，并为总线添加总召唤 */
	iec10x_bus_init(&demobus,104,&ieccfg,device_init);
	iec10xslv_summon_add(&demobus,IEC_GRP_ALL,M_SP_NA_1);

	/* 初始化单点信息的地址 */
	for (int i = 0 ; i < sizeof(SIQaddr)/sizeof(SIQaddr[0]) ; i++) {
		SIQaddr[i] = i + 1;
	}

	pthread_create(&newthread,NULL,iec104_poll  ,NULL);
	pthread_create(&newthread,NULL,socket_init  ,NULL);
	getchar();
	
	#if 0
	SIQ24T_t soe = {0};
	soe.SIQ.val = 1;
	iec10xslv_event(&demobus,M_SP_TA_1,0x001,&soe);
	getchar();
	#endif
	
	return 0;
}
